package tictactoe;

import java.util.Random;


public class AI {
	public AI() {
	}
	
	public int[] move(SquareColor[][][][] board, boolean x_turn, int difficulty) {
        if(difficulty == 0)
        {
            return moveEasy(board,x_turn);
        }
        if(difficulty == 1)
        {
            return moveMedium(board,x_turn);
        }
        if(difficulty == 2)
        {
            return moveHard(board,x_turn);
        }
		return null;
	}

	private int[] moveEasy(SquareColor[][][][] board, boolean x_turn) {
		//dim# specifies what each dimension is doing while checking for rows.
		//Each dimension in a winning board has to either be fixed, increasing or decreasing
		//Numbers >= 0 are treated as fixed coordinates whose value is the same as their dimension
		//-1 represents a coordinate that is increasing, -2 represents a coordinate that is decreasing
		int board_length = board.length;
		int[][][][] weights = new int[4][4][4][4];
		for(int i = 0; i < 4; i++) {
			for(int j = 0; j < 4; j++) {
				for(int k = 0; k < 4; k++) {
					for(int l = 0; l < 4; l++) {
						weights[i][j][k][l] = 0;
					}
				}
			}
		}
		Row row = new Row();
		do {
			int x_count = 0;
			int o_count = 0;
			for(int itr = 0; itr < board_length; itr++) {
				Square s = row.getSquare(itr, board_length);
				SquareColor marker = board[s.coord1][s.coord2][s.coord3][s.coord4];
				if(marker == SquareColor.X) {
					x_count++;
				}
				if(marker == SquareColor.O) {
					o_count++;
				}
			}
			int weight = getWeight(x_count,o_count,x_turn);
			for(int itr = 0; itr < board_length; itr++) {
				Square s = row.getSquare(itr, board_length);
				weights[s.coord1][s.coord2][s.coord3][s.coord4] += weight;
			}
		} while(row.next(board_length));
		int max_weight = -10;
		int[] answer = new int[4];
		for(int dim1 = 0; dim1 < board_length; dim1++) {
			for(int dim2 = 0; dim2 < board_length; dim2++) {
				for(int dim3 = 0; dim3 < board_length; dim3++) {
					for(int dim4 = 0; dim4 < board_length; dim4++) {
						if(weights[dim1][dim2][dim3][dim4] > max_weight && board[dim1][dim2][dim3][dim4] == SquareColor.Empty) {
							max_weight = weights[dim1][dim2][dim3][dim4];
							answer[0] = dim1;
							answer[1] = dim2;
							answer[2] = dim3;
							answer[3] = dim4;
						}
					}	
				}
			}
		}

		return answer;
	}
	
	private int getWeight(int x_count, int o_count, boolean x_turn) {

		if(x_count > 0 && o_count > 0) return 0;
		boolean own_streak_bonus = x_turn;
		int count = x_count;
		if(count == 0) {
			count = o_count;
			own_streak_bonus = true;
		}
		int result = 0;
		if(count == 3) result = 5000;
		else if(count == 2) result = 2000;
		else if(count == 1) result = 500;
		else result = 5;
		if(own_streak_bonus) result += 10;
		return result;
		
	}

	private int[] moveMedium(SquareColor[][][][] board, boolean x_turn) {
		// TODO Auto-generated method stub
		return null;
	}
	
	private int[] moveHard(SquareColor[][][][] board, boolean x_turn) {
		// TODO Auto-generated method stub
		return null;
	}
	
	public int[] monteCarlo(GameLogic g) {
		Square[] s = g.emptySquares();
		int[] scores = new int[s.length];
		boolean x_turn = g.get_x_turn();
		for(int move = 0; move < s.length; move++) {
			System.out.printf("move num: %d\n",move);
			//print(g);
			int num_trials = 1000;
			int score = 0;
			for(int i = 0; i < num_trials; i++) {
				GameLogic copy = g.newInstance();
				//print(copy);
				copy.makeMove(s[move].coord1,s[move].coord2,s[move].coord3,s[move].coord4);
				//print(copy);
				GameStatus winner = simulate(copy);
				//print(copy);
				
				if(winner == GameStatus.Xwin) score++;
				if(winner == GameStatus.Owin) score--;
			}
			// scoring is reversed for second player
			if(!x_turn) score *= -1;
			scores[move] = score;
			System.out.printf("Score %d is %d\n",move,score);
		}
		int best_score = scores[0];
		int best_index = 0;
		for(int move = 0; move < s.length; move++) {
			if(scores[move] > best_score) {
				best_score = scores[move];
				best_index = move;
			}
		}
		int[] result = new int[4];
		result[0] = s[best_index].coord1;
		result[1] = s[best_index].coord2;
		result[2] = s[best_index].coord3;
		result[3] = s[best_index].coord4;
		return result;
	}

	private GameStatus simulate(GameLogic g) {
		while(g.winState() == GameStatus.InProgress) {
			Square[] moves = g.emptySquares();
			Square next_move = moves[rand.nextInt(moves.length)];
			g.makeMove(next_move.coord1, next_move.coord2, next_move.coord3, next_move.coord4);
		}
		return g.winState();
	}

	private GameStatus fastSimulate(GameLogic g) {
		int move_ctr = 0;
		while(g.winState() == GameStatus.InProgress) {
			Square[] moves = g.emptySquares();
			int[] indices = shuffle(moves.length);
			Square next_move = moves[indices[move_ctr]];
			g.makeMove(next_move.coord1, next_move.coord2, next_move.coord3, next_move.coord4);
			move_ctr++;
		}
		//print(g);
		return g.winState();
	}
	
	private GameStatus fastSimulate2(GameLogic g) {
		int move_ctr = 0;
		while(g.winState() == GameStatus.InProgress) {
			Square[] moves = g.emptySquares();
			int[] indices = shuffle(moves.length);
			Square next_move = moves[indices[move_ctr]];
			g.makeMove(next_move.coord1, next_move.coord2, next_move.coord3, next_move.coord4);
			move_ctr++;
		}
		return g.winState();
	}
	private int[] shuffle(int length) {
		int[] result = new int[length];
		for(int i = 0; i < length; i++) {
			result[i] = i;
		}
		for(int i = length - 1; i >= 1; i--) {
			int swap = rand.nextInt(i);
			int temp = result[i];
			result[i] = result[swap];
			result[swap] = temp;
		}
		return result;
	}
	Random rand = new Random();


	public static void print(GameLogic g) {
		//Prints the board
		SquareColor[][][][] board = g.gameState();
		int length = 4;
		for(int coord2 = 0; coord2 < length; coord2++) {
			for(int coord4 = 0; coord4 < length; coord4++) {
				for(int coord1 = 0; coord1 < length; coord1++) {
					for(int coord3 = 0; coord3 < length; coord3++) {
						printSquare(board[coord1][coord2][coord3][coord4]);
						if(coord3 != length - 1) {
							System.out.printf("|");
						}
					}
					System.out.print("  ");
				}
				System.out.println();
				if(coord4 != length-1) {
					for(int coord1 = 0; coord1 < length; coord1++) {
						for(int coord3 = 0; coord3 < length; coord3++) {
							System.out.printf("_");
							if(coord3 != length - 1) {
								System.out.printf(".");
							}
						}
						/*         if(coord1 != length - 1) {
                                            System.out.printf(" ");
                                    }*/
						System.out.print("  ");
					}
				}
				System.out.println();
			}
			System.out.println();
		}
	}   


	public static void printSquare(SquareColor marker) {
		if(marker == SquareColor.X) {
			System.out.print('X');
		}
		else if(marker == SquareColor.O) {
			System.out.print('O');
		}
		else if(marker == SquareColor.Empty) {
			System.out.print(' ');
		}		
	}
}